package com.chenjiaru.thread.lock;

import com.chenjiaru.thread.util.ThreadUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Semaphore实现演示（模拟数据库连接池管理连接的场景）
 */
public class SemaphoreDemo {

    public static void main(String[] arr) {
        ClientManage clientManage = new ClientManage(24);
        for (int i = 0; i < 24; i++) {
            new Worker("线程-" + i, clientManage).start();
        }
    }

}

class Worker extends Thread {
    private ClientManage clientManage;
    public Worker(String name, ClientManage clientManage) {
        setName(name);
        this.clientManage = clientManage;
    }

    @Override
    public void run() {
        this.clientManage.useClient();
    }
}

@Slf4j
class ClientManage {
    private Semaphore semaphore;
    private Client[] clients;
    private ReentrantLock lock;

    public ClientManage(int capacity) {
        if (capacity <= 0) {
            throw new RuntimeException("capacity必须大于0");
        }
        // 初始化capacity个client和许可证
        this.clients = new Client[capacity];
        this.semaphore = new Semaphore(capacity);
        this.lock = new ReentrantLock();
        for (int i = 0; i < capacity; i++) {
            this.clients[i] = new Client(i, true);
        }
    }

    /**
     * 使用client
     */
    public void useClient() {
        Client client = null;
        try {
            semaphore.acquire();
            client = getClient();
            client.process();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 这里相当于归还连接，可供其他线程使用，因为资料是宝贵的所以用完一定要归还
            if (client != null) {
                client.setUsable(true);
            }
            semaphore.release();
        }
    }

    /**
     * 获取可用的client
     * @return
     */
    private Client getClient() {
        Client client = null;
        try {
            // 虽然使用了锁控制同步，但由于只是简单的一个数组遍历，效率还是很高的，所以基本不影响性能。
            lock.lock();
            int len = clients.length;
            for(int i = 0; i < len; i++){
                Client temp = clients[i];
                if(temp.getUsable()){
                    ThreadUtil.mySleep(100);
                    temp.setUsable(false);
                    client = temp;
                    break;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
        return client;
    }
}

@Data
@Slf4j
class Client {
    private Integer id;
    private Boolean usable;

    public Client(Integer id, Boolean usable) {
        this.id = id;
        this.usable = usable;
    }


    public void process() {
        log.info("连接资源{} 使用中...", this.id);
        try {
            Thread.sleep(4000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("连接资源{} 使用结束...", this.id);
    }
}